package drds.plus.datasource.configuration;

import drds.plus.datasource.connection_restrict.ConnectionRestrictEntry;
import drds.tools.$;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

/**
 * TAtom数据源的推送配置解析类
 */
public class DatasourceConfigurationParser {
    public static final String GLOBA_IP_KEY = "ip";
    public static final String GLOBA_PORT_KEY = "port";
    public static final String GLOBA_DB_NAME_KEY = "dbName";
    public static final String GLOBA_DB_STATUS_KEY = "dbStatus";
    public static final String APP_USER_NAME_KEY = "userName";
    public static final String APP_INIT_POOL_SIZE_KEY = "initPoolSize";
    public static final String APP_PREFILL = "prefill";
    public static final String APP_MIN_POOL_SIZE_KEY = "minPoolSize";
    public static final String APP_MAX_POOL_SIZE_KEY = "maxPoolSize";
    public static final String APP_IDLE_TIMEOUT_KEY = "idleTimeout";
    public static final String APP_BLOCKING_TIMEOUT_KEY = "blockingTimeout";
    public static final String APP_CON_PROP_KEY = "connectionProperties";
    public static final String PASSWD_ENC_PASSWD_KEY = "encPasswd";

    public static final String APP_CONNECTION_INIT_SQL_KEY = "connectionInitSql";
    /**
     * 写，次数限制
     */
    public static final String APP_WRITE_RESTRICT_TIMES = "writeRestrictTimes";
    /**
     * 读，次数限制
     */
    public static final String APP_READ_RESTRICT_TIMES = "readRestrictTimes";
    /**
     * thread count 次数限制
     */
    public static final String APP_THREAD_COUNT_RESTRICT = "threadCountRestrict";
    public static final String APP_TIME_SLICE_IN_MILLS = "timeSliceInMillis";
    /**
     * 应用连接限制: 限制某个应用键值的并发连接数。
     */
    public static final String APP_CONN_RESTRICT = "connRestrict";


    public static DatasourceConfiguration parseDatasourceConfiguration(String string) {


        {
            DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
            Properties properties = DatasourceConfigurationParser.parserConfStr2Properties(string);
            String ip = properties.getProperty(DatasourceConfigurationParser.GLOBA_IP_KEY);
            datasourceConfiguration.setIp(ip);
            String port = properties.getProperty(DatasourceConfigurationParser.GLOBA_PORT_KEY);
            datasourceConfiguration.setPort(port);
            String databaseName = properties.getProperty(DatasourceConfigurationParser.GLOBA_DB_NAME_KEY);
            datasourceConfiguration.setDatabaseName(databaseName);
            String dataBaseStatusString = properties.getProperty(DatasourceConfigurationParser.GLOBA_DB_STATUS_KEY);
            datasourceConfiguration.setDataBaseStatusString(dataBaseStatusString);

            String userName = properties.getProperty(DatasourceConfigurationParser.APP_USER_NAME_KEY);
            datasourceConfiguration.setUsername(userName);
            String initPoolSize = properties.getProperty(DatasourceConfigurationParser.APP_INIT_POOL_SIZE_KEY);
            datasourceConfiguration.setInitPoolSize(Integer.valueOf(initPoolSize));
            String minPoolSize = properties.getProperty(DatasourceConfigurationParser.APP_MIN_POOL_SIZE_KEY);
            datasourceConfiguration.setMinPoolSize(Integer.valueOf(minPoolSize));
            String maxPoolSize = properties.getProperty(DatasourceConfigurationParser.APP_MAX_POOL_SIZE_KEY);
            datasourceConfiguration.setMaxPoolSize(Integer.valueOf(maxPoolSize));
            String idleTimeout = properties.getProperty(DatasourceConfigurationParser.APP_IDLE_TIMEOUT_KEY);
            datasourceConfiguration.setIdleTimeout(Long.valueOf(idleTimeout));
            String blockingTimeout = properties.getProperty(DatasourceConfigurationParser.APP_BLOCKING_TIMEOUT_KEY);
            datasourceConfiguration.setBlockingTimeout(Integer.valueOf(blockingTimeout));
            String writeRestrictTimes = properties.getProperty(DatasourceConfigurationParser.APP_WRITE_RESTRICT_TIMES);
            datasourceConfiguration.setWriteRestrictTimes(Integer.valueOf(writeRestrictTimes));
            String readRestrictTimes = properties.getProperty(DatasourceConfigurationParser.APP_READ_RESTRICT_TIMES);
            datasourceConfiguration.setReadRestrictTimes(Integer.valueOf(readRestrictTimes));
            String threadCountRestrict = properties.getProperty(DatasourceConfigurationParser.APP_THREAD_COUNT_RESTRICT);
            datasourceConfiguration.setThreadCountRestrict(Integer.valueOf(threadCountRestrict));
            String timeSliceInMillis = properties.getProperty(DatasourceConfigurationParser.APP_TIME_SLICE_IN_MILLS);
            datasourceConfiguration.setTimeSliceInMillis(Integer.valueOf(timeSliceInMillis));
            String conPropStr = properties.getProperty(DatasourceConfigurationParser.APP_CON_PROP_KEY);
            Map<String, String> connectionProperties = parserConPropStr2Map(conPropStr);
            if (null != connectionProperties && !connectionProperties.isEmpty()) {
                datasourceConfiguration.setConnectionProperties(connectionProperties);
                if (connectionProperties.containsKey(APP_PREFILL)) {
                    String prefill = connectionProperties.remove(APP_PREFILL);
                    if (Boolean.parseBoolean(prefill) && datasourceConfiguration.getInitPoolSize() == DatasourceConfiguration.defaultInitPoolSize) {
                        datasourceConfiguration.setInitPoolSize(datasourceConfiguration.getMinPoolSize());
                    }
                }
                String connectionInitSql = connectionProperties.remove(DatasourceConfigurationParser.APP_CONNECTION_INIT_SQL_KEY);
                if (!$.isNullOrEmpty(connectionInitSql)) {
                    datasourceConfiguration.setConnectionInitSql(connectionInitSql);
                }
            }
            // 解析应用连接限制, 参看下面的文档
            String connRestrictStr = properties.getProperty(DatasourceConfigurationParser.APP_CONN_RESTRICT);
            List<ConnectionRestrictEntry> connectionRestrictEntryList = parseConnRestrictEntries(connRestrictStr, datasourceConfiguration.getMaxPoolSize());
            if (null != connectionRestrictEntryList && !connectionRestrictEntryList.isEmpty()) {
                datasourceConfiguration.setConnectionRestrictEntryList(connectionRestrictEntryList);
            }
            return datasourceConfiguration;
        }

    }

    public static Map<String, String> parserConPropStr2Map(String conPropStr) {
        Map<String, String> connectionProperties = null;
        if ($.isNotNullAndNotEmpty(conPropStr)) {
            String[] keyValues = conPropStr.split(";");
            if (null != keyValues && keyValues.length > 0) {
                connectionProperties = new HashMap<String, String>(keyValues.length);
                for (String keyValue : keyValues) {
                    String key = "";//TStringUtil.substringBefore(keyValue, "=");
                    String value = "";// TStringUtil.substringAfter(keyValue, "=");
                    connectionProperties.put(key.trim(), value.trim());
                }
            }
        }
        return connectionProperties;
    }


    public static Properties parserConfStr2Properties(String s) {
        Properties properties = new Properties();
        if ($.isNotNullAndNotEmpty(s)) {
            ByteArrayInputStream byteArrayInputStream = null;
            try {
                byteArrayInputStream = new ByteArrayInputStream((s).getBytes());
                properties.load(byteArrayInputStream);
            } catch (IOException e) {
                // log.error("parserConfStr2Properties Error");
            } finally {
                try {
                    byteArrayInputStream.close();
                } catch (IOException e) {

                }
            }
        }
        return properties;
    }

    /**
     * 解析应用连接限制, 完整格式是: "K1,K2,K3,K4:80%; K5,K6,K7,K8:80%; K9,K10,K11,K12:80%;
     * *:16,80%; ~:80%;" 这样可以兼容 HASH: "*:16,80%", 也可以兼容 LIST: "K1:80%; K2:80%;
     * K3:80%; K4:80%; ~:80%;" 配置可以是连接数, 也可以是百分比。
     */
    public static List<ConnectionRestrictEntry> parseConnRestrictEntries(String connRestrictStr, int maxPoolSize) {
        List<ConnectionRestrictEntry> connectionRestrictEntryList = null;
        if ($.isNotNullAndNotEmpty(connRestrictStr)) {
            // Split "K1:number1; K2:number2; ...; *:count,number3; ~:number4"
            String[] entries = connRestrictStr.split(";");
            if (null != entries && entries.length > 0) {
                HashMap<String, String> existKeys = new HashMap<String, String>();
                connectionRestrictEntryList = new ArrayList<ConnectionRestrictEntry>(entries.length);
                for (String entry : entries) {
                    // Parse "K1,K2,K3:number | *:count,number | ~:number"
                    int find = entry.indexOf(':');
                    if (find >= 1 && find < (entry.length() - 1)) {
                        String key = entry.substring(0, find).trim();
                        String value = entry.substring(find + 1).trim();
                        // "K1,K2,K3:number | *:count,number | ~:number"
                        ConnectionRestrictEntry connectionRestrictEntry = ConnectionRestrictEntry.parse(key, value, maxPoolSize);
                        if (connectionRestrictEntry == null) {
                            // log.error("[connRestrict Error] parser entry error: " + entry);
                        } else {
                            // Remark entry configuration problem
                            if (0 >= connectionRestrictEntry.getLimits()) {
                                // log.error("[connRestrict Error] connection limit is 0: " + entry);
                                connectionRestrictEntry.setLimits(/* 至少允许一个连接 */1);
                            }
                            if (ConnectionRestrictEntry.MAX_HASH_RESTRICT_SLOT < connectionRestrictEntry.getMaxHashSlotSize()) {
                                // log.error("[connRestrict Error] hash size exceed maximum: " + entry);
                                connectionRestrictEntry.setMaxHashSlotSize(ConnectionRestrictEntry.MAX_HASH_RESTRICT_SLOT);
                            }
                            // Remark Key configuration confliction
                            for (String slotKey : connectionRestrictEntry.getSlotKeys()) {
                                if (!existKeys.containsKey(slotKey)) {
                                    existKeys.put(slotKey, entry);
                                } else if (ConnectionRestrictEntry.isWildcard(slotKey)) {
                                    // log.error("[connRestrict Error] hash configuration [" + entry + "] conflict with ["
                                    // + existKeys.get(slotKey) + "]");
                                } else if (ConnectionRestrictEntry.isNullKey(slotKey)) {
                                    // log.error("[connRestrict Error] null-key configuration [" + entry + "] conflict with
                                    // [" + existKeys.get(slotKey) + "]");
                                } else {
                                    // log.error("[connRestrict Error] " + slotKey + " configuration [" + entry+ "]
                                    // conflict with [" + existKeys.get(slotKey) + "]");
                                }
                            }
                            connectionRestrictEntryList.add(connectionRestrictEntry);
                        }
                    } else {
                        // log.error("[connRestrict Error] unknown entry: " + entry);
                    }
                }
            }
        }
        return connectionRestrictEntryList;
    }
}
